﻿using Devil;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEngine;

namespace GameToolkit.Editor
{
    public static class StoryGraphUtils
    {
       
        static T GetNodeAsset<T>(this Node node) where T: TaskGraphAsset.NodeAsset
        {
            if (node is IStoryNodeVisual sv)
                return sv.nodeAsset as T;
            else
                return default;
        }

        static int GetPortID(this Port port)
        {
            if (port is ISingleConnectionBetweenNodes gp)
                return gp.Id;
            else
                return 0;
        }

        internal static void Save(this StoryGraph graph, TaskGraphAsset asset)
        {
            var graphnodes = new List<Node>();
            var nodes = new List<TaskGraphAsset.NodeAsset>();
            //var groups = new List<BehaviourTreeAsset.Group>();
            int activeBuffer = graph.startNode.actionPort.connections.Count();
            int taskCount = 0;
            asset.m_StartNode = -1;
            foreach (var item in graph.graphElements)
            {
                var node = item as Node;
                if (item is IStoryNodeVisual snode)
                {
                    var nodeasset = snode.nodeAsset;
                    if (nodeasset != null)
                    {
                        graphnodes.Add((Node)item);
                        nodeasset.m_Position = snode.position;
                        nodes.Add(nodeasset);
                        if (nodeasset is TaskNode tnode)
                        {
                            taskCount++;
                            var vnode = (StoryNodeVisual)item;
                            tnode.m_ChildrenCount = 0;
                            for (int i = node.outputContainer.childCount - 1; i >= 0; i--)
                            {
                                var port = node.outputContainer[i] as Port;
                                if (port != null)
                                    tnode.m_ChildrenCount += port.connections.Count();
                            }
                            //if(vnode.exitPort != null)
                            //    tnode.m_ChildrenCount += vnode.exitPort.connections.Count();
                            //if (vnode.successPort != null)
                            //    tnode.m_ChildrenCount += vnode.successPort.connections.Count();
                            //if (vnode.failPort != null)
                            //    tnode.m_ChildrenCount += vnode.failPort.connections.Count();
                        }
                    }
                }
            }
            foreach(var conn in graph.startNode.actionPort.connections)
            {
                var node = conn.input.node.GetNodeAsset<TaskNode>();
                asset.m_StartNode = node == null ? -1 : nodes.IndexOf(node);
                if (asset.m_StartNode != -1)
                    break;
            }
            var preconditions = new List<TaskNode.Precondition>();
            var mutexGroup = new List<TaskNode>();
            foreach(var item in graphnodes)
            {
                if (item is StoryNodeVisual snode)
                {
                    var task = snode.GetNodeAsset<TaskNode>();
                    if (task == null)
                        continue;
                    preconditions.Clear();
                    foreach (var conn in snode.startPort.connections)
                    {
                        var prenode = conn.output.node as StoryNodeVisual;
                        TaskNode.Precondition prec;
                        prec.node = prenode.GetNodeAsset<TaskNode>();
                        if (prenode == null || prec.node == null)
                            continue;
                        prec.result = conn.output.GetPortID();// conn.output == prenode.exitPort ? exitFlag : (conn.output == prenode.successPort ? EFinishFlag.OnSuccess : EFinishFlag.OnFailed);
                        preconditions.Add(prec);
                        //if (task is FinalTaskNode)
                        //    prec.node.m_FinishFlag |= prec.flags;
                    }
                    task.m_Preconditions = preconditions.ToArray();
                    preconditions.Clear();
                    if (snode.killPort != null)
                    {
                        foreach (var conn in snode.killPort.connections)
                        {
                            var prenode = conn.output.node as StoryNodeVisual;
                            TaskNode.Precondition prec;
                            prec.node = prenode.GetNodeAsset<TaskNode>();
                            if (prenode == null || prec.node == null)
                                continue;
                            prec.result = conn.output.GetPortID();// conn.output == prenode.exitPort ? exitFlag : (conn.output == prenode.successPort ? EFinishFlag.OnSuccess : EFinishFlag.OnFailed);
                            preconditions.Add(prec);
                        }
                    }
                    task.m_Killers = preconditions.ToArray();
                    int num1 = 0, num2 = 0;
                    for (int i = snode.outputContainer.childCount - 1; i >= 0; i--)
                    {
                        var port = snode.outputContainer[i] as Port;
                        if (port == null)
                            continue;
                        if (port.GetPortID() == 0)
                            num1 = port.connections.Count();
                        else
                            num2 = Mathf.Max(port.connections.Count());
                    }
                    activeBuffer = Mathf.Max(activeBuffer, num1 + num2);
                }
            }
            asset.m_StartPosition = graph.startNode.GetPosition().position;
            asset.m_Nodes = nodes.ToArray();
            asset.m_ActiveBufferSize = activeBuffer;
            asset.m_TaskCount = taskCount;
            var path = AssetDatabase.GetAssetPath(asset);
            if (string.IsNullOrEmpty(path) || !path.EndsWithIgnoreCase(".storygraph"))
            {
                asset.hideFlags |= UnityEngine.HideFlags.NotEditable;
                EditorUtility.SetDirty(asset);
                AssetDatabase.SaveAssetIfDirty(asset);
            }
            else
            {
                var data = JsonSerializedObject.ExportFrom(asset);
                NativeFileSystem.WriteAllText(path, data.ToJson(), Encoding.UTF8);
                AssetDatabase.ImportAsset(path);
            }
        }

    }
}
